﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace MathTools
{
    public abstract class AbstractCalculator
    {
        #region Fields

        private Thread invokatingThread;

        private double eps;

        #endregion

        #region Events

        // Declare the delegate (if using non-generic pattern).
        public delegate void FinishedHandler( double[ , ] anwers );

        // Declare the event.
        public event FinishedHandler FinishedEvent;

        #endregion

        public AbstractCalculator()
        {
            isStopped = true;

            Sigma = 10;
            B = 8 / 3;
            R = 0;

            X0 = Y0 = Z0 = 1;

            MustStop = false;

            MaxCount = int.MaxValue;
            Step = 1e-1;
        }

        #region Running control

        protected bool MustStop
        {
            get;
            private set;
        }

        public void beginSolveAsync( double eps )
        {
            this.eps = eps;

            invokatingThread = new Thread( run );
            invokatingThread.IsBackground = true;
            invokatingThread.Start( this );

            return;
        }

        public double[ , ] endSolveAsync( bool interrupt )
        {
            if( !invokatingThread.IsAlive )
                return Answers;

            invokatingThread.IsBackground = false;

            if( interrupt )
            {
                MustStop = true;

                Thread.Sleep( 2000 );

                if( invokatingThread.ThreadState != ThreadState.Stopped && !isStopped )
                    invokatingThread.Abort();
            }
            else
            {
                while( invokatingThread.ThreadState == ThreadState.Running )
                    Thread.Sleep( 100 );
            }

            return Answers;
        }

        public abstract int CurrentStep
        {
            get;
        }

        public bool isStopped
        {
            get;

            private set;
        }

        #endregion

        #region Calculate control

        protected abstract double[ , ] solve( double eps );

        public double X0
        {
            get;
            set;
        }

        public double Y0
        {
            get;
            set;
        }

        public double Z0
        {
            get;
            set;
        }

        public double Sigma
        {
            get;
            set;
        }

        public double B
        {
            get;
            set;
        }

        public double R
        {
            get;
            set;
        }

        public double[ , ] Answers
        {
            get;

            private set;
        }

        public double Step
        {
            get;
            set;
        }

        public int MaxCount
        {
            get;
            set;
        }
        #endregion

        private static void run( object obj )
        {
            if( !(obj is AbstractCalculator) )
                return;            

            AbstractCalculator calc = obj as AbstractCalculator;

            calc.isStopped = false;
            calc.MustStop = false;

            try
            {
                double[ , ] ans = calc.solve( calc.eps );

                int lenght = ans.GetLength( 1 );
                int dim = ans.GetLength( 0 );

                if( lenght > 20000 )
                {
                    int step = lenght / 10000;
                    calc.Answers = new double[ dim, lenght / step ];
                    for( int i = 0; i < lenght / step; i++ )
                    {
                        for( int j = 0; j < dim; j++ )
                            calc.Answers[ j, i ] = ans[ j, i * step ];
                    }
                }
                else
                {
                    calc.Answers = ans;
                }
            }
            catch( OutOfMemoryException )
            {
                /***/
                calc.isStopped = true;

                calc.Answers = null;

                return;
            }

            calc.isStopped = true;

            calc.FinishedEvent( calc.Answers );
        }

        protected double FX( double x, double y, double z )
        {
            return -Sigma * x + Sigma * y;
        }

        protected double FY( double x, double y, double z )
        {
            return -x * z + R * x - y;
        }

        protected double FZ( double x, double y, double z )
        {
            return x * y - B * z;
        }
    }
}
